#include "guish.hpp"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

FileSender::FileSender(ClientConnection *connection, int fd, StreamSender *sendStream, Job *ownerJob)
{
	this->clientConnection = connection;
	this->id = -1;
	this->fd = fd;
	this->sendStream = sendStream;
	this->ownerJob = ownerJob;
	
	fileSize = (size_t)lseek(fd, 0, SEEK_END);
	lseek(fd, 0, SEEK_SET);
}

FileSender::FileSender(ClientConnection *connection, int id, string path)
{
	this->clientConnection = connection;
	this->id = id;
	this->ownerJob = NULL;
	
	fd = open(path.c_str(), O_RDONLY);
	
	if(fd<0)
	{
		string errorMessage = path+": "+strerror(errno);
		
		RPCParams getFailedParams;
			getFailedParams.set("id", id);
			getFailedParams.set("filename", path);
			getFailedParams.set("message", errorMessage);
		clientConnection->call("getfailed", &getFailedParams);
		
		sendStream = NULL;
		// TODO: Clean up when finished
	}
	else
	{
		fileSize = (size_t)lseek(fd, 0, SEEK_END);
		lseek(fd, 0, SEEK_SET);
		
		RPCParams getResponseParams;
			getResponseParams.set("id", id);
			getResponseParams.set("filename", path);
			getResponseParams.set("size", (int64_t)fileSize);
			this->sendStream = new StreamSender(connection, &getResponseParams);
		clientConnection->call("getresp", &getResponseParams);
	}
}

FileSender::~FileSender()
{
	closeFile();
}

void FileSender::cancel()
{
	closeFile();
}

size_t FileSender::getFileSize()
{
	return fileSize;
}

bool FileSender::isFinished()
{
	return(fd<0);
}

void FileSender::prepareSelect(int priority, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, int *maxfd)
{
	if(priority < 3)
		return;
	if(fd < 0)
		return;
	
	if(clientConnection->isIdle()) {
		FD_SET(fd, readfds);
		if(fd>*maxfd) *maxfd = fd;
	}
}

void FileSender::postSelect(int priority, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
{
	if(priority < 3)
		return;
	if(fd < 0)
		return;
	if(!sendStream)
		return;
	
	if(FD_ISSET(fd, readfds))
	{
		char buf[8192];
		int bytesRead = read(fd, buf, 8192);
		
		if(bytesRead > 0) {
			sendStream->send(bytesRead, buf);
		} else if(bytesRead==0) {
			// End of file
			closeFile();
		} else {
			// Read error
			if(errno != EINTR && errno != EAGAIN) {
				closeFile();
			}
		}
	}
}

void FileSender::closeFile()
{
	// TODO: Clean up better when finished (delete this class)
	
	if(fd<0) return;
	
	sendStream->close();
	sendStream = NULL;
	
	close(fd);
	fd = -1;
	
	if(ownerJob) {
		ownerJob->sendEvent(new FileTransferFinishedEvent());
	}
}

//////////////////////////////////////////////////////////////////////////////

static map<int, FileSender*> fileSenders;

RPC_HANDLER(get)
{
	int id = params->getInt("id");
	string path = params->getString("path");
	fileSenders[id] = new FileSender(clientConnection, id, path);
}

RPC_HANDLER(getcancel)
{
	int id = params->getInt("id");
	if(fileSenders.find(id) != fileSenders.end()) {
		fileSenders[id]->cancel();
	}
}

